package com.bestcem.xm.member.handler.imports;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bestcem.xm.common.core.constant.AppRequestErrorCode;
import com.bestcem.xm.common.core.constant.AppResourceErrorCode;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.enums.ResourceErrorCodeEnum;
import com.bestcem.xm.common.core.enums.ValidationErrorCodeEnum;
import com.bestcem.xm.common.core.uitls.FileUtil;
import com.bestcem.xm.member.dao.ProjectDao;
import com.bestcem.xm.member.entity.pojo.ExtendFieldEnumItemDO;
import com.bestcem.xm.member.entity.pojo.ImportRecordDo;
import com.bestcem.xm.member.enums.ImportRecordStatusEnum;
import com.bestcem.xm.member.grpc.service.param.imports.ImportMembersParam;
import com.bestcem.xm.member.handler.imports.easyexcel.HttpReadProjectListener;
import com.bestcem.xm.member.service.ProjectService;
import com.bestcem.xm.member.service.dto.FieldDTO;
import com.bestcem.xm.member.service.param.ImportMemberParam;
import com.bestcem.xm.member.service.param.ImportProjectParam;
import com.bestcem.xm.member.util.AsyncUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.bestcem.xm.member.constant.Constants.M_FIELD_ATTRIBUTE_PROJECT;
import static com.bestcem.xm.member.constant.Constants.M_PROJECT_BASE_EXCEL_HEADERS;

/**
 * 企业项目导入
 *
 * @author yf.wang <yf.wang@idiaoyan.com>
 * @version V1.0
 * @date 2021/7/20
 */

@Slf4j
@Component
public class ProjectImportHandler extends BaseImportHandle {

    @Resource
    private ProjectDao projectDao;

    @Resource
    private ProjectService projectService;

    @Override
    public String getTemplateExcelPath() {
        return "/file/excel/projectImportTemplate.xlsx";
    }

    @Override
    public String getExcelTemplateName() {
        return "projectImportTemplate.xlsx";
    }

    /**
     * 项目信息基础表头  扩展字段
     *
     * @param orgId
     * @return
     */
    @Override
    public List<String> getHeader(String orgId) {
        // 获取该组织下所有的扩展字段
        List<String> fieldNames = getFieldNames(orgId, 3);
        int size = M_PROJECT_BASE_EXCEL_HEADERS.size() + fieldNames.size();
        List<String> headers = new ArrayList<>(size);
        // 机构联系人基础字段
        headers.addAll(M_PROJECT_BASE_EXCEL_HEADERS);
        // 扩展字段
        headers.addAll(fieldNames);
        return headers;
    }

    @Override
    public List<String> getExcelHeaders() throws IOException {
        return getTemplateHeaders(getTemplateExcelPath());
    }

    @Override
    public String genImportLockKey(String orgId) {
        return "member.mproject." + orgId;
    }

    @Override
    public ServiceResult<String> importMemberExcel(ImportMembersParam param) {
        return ServiceResult.success();
    }

    @Override
    protected void importMemberExcel(ImportMemberParam param) {

    }

    /**
     * 项目导入
     *
     * @param param
     * @return
     */
    public ServiceResult<String> importProject(ImportProjectParam param) {
        // 先检查是否存在已有的任务
        String key = genImportLockKey(param.getOrgId());
        param.setKey(key);
        // 预先生成导入记录 ObjectId
        String importRecordId = genImportRecordId();
        param.setImportRecordId(importRecordId);
        // 检查是否有导入任务
        if (checkImportTask(key)) {
            return ServiceResult.<String>fail("当前已有项目信息的导入任务正在执行中，请稍后再试").setErrorCode(String.valueOf(AppResourceErrorCode.TOO_BUSY));
        }
        // 加锁
        lockImport(key, importRecordId);
        // 文件流
        MultipartFile files = param.getFiles();
        if (ObjectUtil.isEmpty(files)) {
            log.error("导入的文件不存在");
            // 失败的话 先删除锁
            removeLock(key);
            return ServiceResult.<String>fail("invalid file")
                    .setErrorCode(String.valueOf(AppRequestErrorCode.PARAM_FORBIDDEN));
        }

        //生成导入记录
        ImportRecordDo importRecord = buildMProjectImportRecord(param);
        initImportRecord(importRecord);
        // 文件路径
        String fileUrl = param.getFileUrl();
        if (!files.isEmpty()) {
            // 获取文件名称
            String fileName = files.getOriginalFilename();
            if (StrUtil.isNotEmpty(fileName)) {
                // 若存在文件名 则更新导入记录中的文件名
                importRecordDao.updateImportRecordFileName(param.getImportRecordId(), fileName);
            }
            String fileType = FileUtil.getFileExtension(fileName);
            if (!"xlsx".equals(fileType)) {
                // 文件类型不正确
                log.error("【member excel 导入】文件类型不正确 期望是 {} 实际是 {}", "xlsx", fileType);
                importRecordDao.updateRecordStatus(importRecordId, ImportRecordStatusEnum.IMPORT_RECORD_STATUS_FAILED.getIndex());
                importRecordDao.insertRecordDeatil(importRecordId, -1, "不支持的导入类型");
                // 解锁
                removeLock(key);
                return ServiceResult.<String>fail("不支持的导入类型").setErrorCode(String.valueOf(ValidationErrorCodeEnum.FILE_TYPE_NOT_RIGHT.getCode()));
            }

            // 获取文件大小
            long size = files.getSize();
            if (!FileUtil.checkFileSize(size, 10, FileUtil.M)) {
                // 限制10M
                log.error("【member excel 导入】文件过大");
                importRecordDao.updateRecordStatus(importRecordId, ImportRecordStatusEnum.IMPORT_RECORD_STATUS_FAILED.getIndex());
                importRecordDao.insertRecordDeatil(importRecordId, -1, "文件过大");
                // 解锁
                removeLock(key);
                return ServiceResult.<String>fail("文件过大").setErrorCode(String.valueOf(ResourceErrorCodeEnum.FILE_TOO_LARGE.getCode()));
            }

            // 判断是否为 xlsx 文件
            InputStream inputStream = null;
            try {
                inputStream = files.getInputStream();
            } catch (IOException e) {
                log.error("【member】读取文件失败", e);
                // 更新记录状态为失败
                importRecordDao.updateRecordStatus(importRecordId, ImportRecordStatusEnum.IMPORT_RECORD_STATUS_FAILED.getIndex());
                importRecordDao.insertRecordDeatil(importRecordId, -1, "读取文件失败");
                // 解锁
                removeLock(key);
                return ServiceResult.<String>fail("读取文件失败").setErrorCode("-1");
            }
            param.setInputStream(inputStream);
            AsyncUtil.threadPoolExecutor.execute(() -> importMpExcel(param));
        } else if (StrUtil.isNotEmpty(fileUrl)) {
            String fileName = cn.hutool.core.io.FileUtil.getName(fileUrl);
            if (StrUtil.isNotEmpty(fileName)) {
                importRecordDao.updateImportRecordFileName(param.getImportRecordId(), fileName);
            }
            if (fileName.toLowerCase().contains("xlsx")) {
                // 根据excel导入
                // 异步
                importMpExcel(param);
            } else {
                importRecordDao.updateRecordStatus(importRecordId, ImportRecordStatusEnum.IMPORT_RECORD_STATUS_FAILED.getIndex());
                // 解锁
                removeLock(key);
                return ServiceResult.<String>fail("不支持的导入类型").setErrorCode("400");
            }
        } else {
            // 文件为空
            // 更新记录状态为失败
            importRecordDao.updateRecordStatus(importRecordId, ImportRecordStatusEnum.IMPORT_RECORD_STATUS_FAILED.getIndex());
            importRecordDao.insertRecordDeatil(importRecordId, -1, "文件为空");
            // 解锁
            removeLock(key);
            importService.deleteProgressBarStatus(0, param.getImportRecordId());
            return ServiceResult.<String>fail("文件为空").setErrorCode("-1");
        }
        return ServiceResult.success(importRecordId);
    }

    /**
     * 导入项目
     *
     * @param param
     */
    private void importMpExcel(ImportProjectParam param) {
        if (param.getImportType() == 0) {
            // 增量导入
            incrementImportExcel(param);
        } else {
            // 全量导入
            overwriteImportExcel(param);
        }
    }

    /**
     * 增量导入
     *
     * @param param
     */
    private void incrementImportExcel(ImportProjectParam param) {
        importMProject(param);
    }

    /**
     * 全量覆盖导入
     *
     * @param param
     */
    private void overwriteImportExcel(ImportProjectParam param) {
        // 删除该组织下所有的项目
        projectDao.deleteByOrgId(param.getOrgId());
        incrementImportExcel(param);
    }

    /**
     * 项目导入
     *
     * @param param
     */
    private void importMProject(ImportProjectParam param) {
        ExcelReader excelReader = null;
        try {
            // 获取扩展字段map
            Map<String, FieldDTO> filedMap = getFiledMap(param.getOrgId(), M_FIELD_ATTRIBUTE_PROJECT);
            param.setFiledMap(filedMap);
            // 获取扩展字段对应的枚举类型
            Map<String, List<ExtendFieldEnumItemDO>> extendFieldEnumMap = getExtendFieldEnumMap(filedMap);
            param.setExtendFieldEnumMap(extendFieldEnumMap);

            if (!checkOrganization(param.getOrgId())) {
                // 公司不存在
                throw new IllegalArgumentException("【member】该组织不存在");
            }
            //  easyExcel 读文件
            HttpReadProjectListener listener = new HttpReadProjectListener();
            if (ObjectUtil.isNotEmpty(param.getInputStream())) {
                excelReader = EasyExcel.read(param.getInputStream()).build();
            } else if (StrUtil.isNotEmpty(param.getFileUrl())) {
                excelReader = EasyExcel.read(param.getFileUrl()).build();
            }
            // 参数
            listener.setParam(param);
            // memberService
            listener.setProjectService(projectService);
            // importService
            listener.setImportService(importService);
            // memberService
            listener.setMemberService(memberService);
            // 是否获取过所有数量
            listener.setGetCount(false);
            // 是否终止读取excel
            listener.setIsStop(false);
            listener.setErrorMsgJsons(new JSONArray());
            // 读取第一个sheet
            ReadSheet readSheet = EasyExcel.readSheet(0).registerReadListener(listener).build();
            excelReader.read(readSheet);
        } catch (Exception e) {
            log.error("【member】文件导入异常", e);
            JSONObject totalErrorMsg = new JSONObject();
            JSONArray errorArray = new JSONArray();
            totalErrorMsg.put("-1", e.getMessage());
            errorArray.add(totalErrorMsg);
            Integer receiveCount = 0;
            Integer successCount = 0;
            Integer failedCount = totalErrorMsg.size();
            updateRecordData(param.getImportRecordId(), receiveCount, successCount, failedCount, errorArray, ImportRecordStatusEnum.IMPORT_RECORD_STATUS_FAILED.getIndex());
        } finally {
            if (excelReader != null) {
                excelReader.finish();
            }

            if (param.getInputStream() != null) {
                try {
                    param.getInputStream().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            // 删除key
            // 解锁
            removeLock(param.getKey());
        }
    }

}
